# qpy:webapp:Hello Qpython
# qpy://127.0.0.1:8080/
"""
This is a sample for qpython webapp
"""

from bottle import Bottle, ServerAdapter, response
from bottle import run, debug, route, error, static_file, template
import os
import re
from requests.packages.urllib3.util import Retry
from requests.adapters import HTTPAdapter
from requests import Session, exceptions
from urllib.parse import urlencode
from urllib.request import Request
import time


# util method
du_token = None
du_token_expire = 0.0
APP_ID = 11676745
API_KEY = 'oNwWGhmvbNUcSZEZs6IRndbL'
SECRET_KEY = 'eKDi0haei36jIzGI0UHCupj8e8dCnYeO'
SPD = '7'
VOL = 5
AUE = 3
TTS_URL = 'http://tsn.baidu.com/text2audio'
PER = '5118'


def get_item_info(item_id, text_len_limit=850):
    u = 'http://zjl.hmxingkong.com/n/novel/getItemContent?itemId='
    u += item_id
    s = Session()
    s.mount('http://', HTTPAdapter(
                max_retries=Retry(total=5, status_forcelist=[500])
        )
    )
    r = s.get(u, timeout=15)

    json_data = r.json()
    next_id = json_data['data']['novelItem']['nextId']
    pre_id = json_data['data']['novelItem']['preId']
    title = json_data['data']['novelItem']['title']
    print('next_id\t', next_id)
    text = json_data['data']['content']
    if text is None or text == '':
        u = 'http://zjl.hmxingkong.com/n/novel/content/' + item_id
        r = s.get(u, timeout=20)
        text = r.text
    else:
        text = title + '\n' + text
    text = text.replace('&nbsp;', '').replace('<br/>', '\n')
    text = text.replace('<br />', '\n')
    pats = ['(<title>.*?</title>)', '(<.*?>)']
    for pat in pats:
        results = re.compile(pat, re.S).findall(text)
        for result in results:
            text = text.replace(result, '')

    splits = text.split('。')
    for i in range(0, len(splits)):
        splits[i] = splits[i] + '。'
    splits.append('本章完')
    contents = []
    i = 0
    length = len(splits)
    while i < length:
        c_temp = splits[i]
        i += 1
        while i < length and len(c_temp) + len(splits[i]) < text_len_limit:
            c_temp += splits[i]
            i += 1
        for n in range(0, int(len(c_temp) / text_len_limit + 1)):
            contents.append(c_temp[n * text_len_limit:n * text_len_limit + text_len_limit])
    result = {'next_id': next_id, 'pre_id': pre_id, 'title': title, 'contents': contents}
    s.close()
    return result


def update_token():
    global du_token
    global du_token_expire
    u = 'https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id='\
        + API_KEY + '&client_secret=' + SECRET_KEY
    s = Session()
    s.mount('http://', HTTPAdapter(
        max_retries=Retry(total=5, status_forcelist=[500])
    )
            )
    u = 'http://zjl.carfunny.com.cn/proxy?u=' + u.replace('&', '%26')
    r = s.get(u, timeout=10)
    j = r.json()
    print(j)
    if 'access_token' in j:
        du_token = j['access_token']
    # cache_data.du_token = j['refresh_token']
    du_token_expire = time.time() + j['expires_in']
    s.close()


def synthesis(tex, cuid='zh', spd='5', pit='5', vol='5', per='0'):
    if du_token is None or du_token_expire is None or du_token_expire < time.time():
        update_token()
    params = {'tok': du_token, 'tex': tex, 'per': per, 'spd': spd, 'pit': pit, 'vol': vol, 'aue': AUE, 'cuid': cuid,
              'lan': 'zh', 'ctp': 1}  # lan ctp 固定参数

    # params = {'tok': cache_data.du_token, 'tex': tex, 'spd': spd, 'pit': pit, 'vol': vol, 'aue': constants.AUE,
    # 'cuid': cuid,
    #           'lan': 'zh', 'ctp': 1}  # lan ctp 固定参数

    data = urlencode(params)

    req = Request(TTS_URL, data.encode('utf-8'))
    s = Session()
    s.mount('http://', HTTPAdapter(
        max_retries=Retry(total=5, status_forcelist=[500])
    )
            )
    r = s.get(req.full_url + '?' + req.data.decode(), timeout=10)
    headers = dict((name.lower(), value) for name, value in r.headers.items())
    # print('headers:   ', headers)
    if 'content-type' in headers and (headers['content-type'] == 'application/json'
                                      or headers['content-type'] == 'text/html'):
        print('[error] text length:', len(tex), r.text)
        return []
    b = r.content
    s.close()
    return b


# ######## WEB SERVER ###############

class MyWSGIRefServer(ServerAdapter):
    server = None

    def run(self, handler):
        from wsgiref.simple_server import make_server, WSGIRequestHandler
        if self.quiet:
            class QuietHandler(WSGIRequestHandler):
                def log_request(*args, **kw): pass
            self.options['handler_class'] = QuietHandler
        self.server = make_server(self.host, self.port, handler, **self.options)
        self.server.serve_forever()

    def stop(self):
        # sys.stderr.close()
        import threading 
        threading.Thread(target=self.server.shutdown).start() 
        # self.server.shutdown()
        self.server.server_close()  # <--- alternative but causes bad fd exception
        print("# server stop")


# ######## BUILT-IN ROUTERS ###############
@route('/__exit', method=['GET', 'HEAD'])
def __exit():
    global server
    server.stop()


@route('/__ping')
def __ping():
    return "ok"


@route('/assets/<file_path:path>')
def server_static(file_path):
    file_path_root = 'd:/video'
    # file_path_root = './'
    print('file_path', file_path)
    full_file_path = file_path_root + '/' + file_path
    if os.path.isdir(full_file_path):
        html = ''
        file_list = os.listdir(full_file_path)
        html += '<h6><a href="../" >..</a></h6>'
        for f in file_list:
            html += '\n<h6><a href="' + file_path + '/' + f + '" >' + f + '</a></h6>'
        return template(html)
    
    # return static_file(file_path, root=file_path_root)
    response.content_type = 'video/mp4'
    return get_file_data(full_file_path)
    # f = open(full_file_path, 'rb')
    # chunk = 10240
    # while True:
    #     if not f.readable():
    #         f.close()
    #         return
    #     b = f.read(chunk)
    #     # b = f.readline(chunk)
    #     yield b


def get_file_data(file_path):
    f = open(file_path, 'rb')
    chunk = 10240
    while True:
        if not f.readable():
            f.close()
            return
        b = f.read(chunk)
        # b = f.readline(chunk)
        yield b


@route('/np/<file_path:path>')
def novel(file_path):
    file_path_root = 'D:/code/zjl-novel-front/zjl-novel-front/public_html'
    print('file_path', file_path)
    if file_path.endswith('/') or file_path == '':
        file_path += 'index.html'
    return static_file(file_path, root=file_path_root)
    
    
@route('/np/')
def novel_index():
    file_path_root = 'D:/code/zjl-novel-front/zjl-novel-front/public_html'
    return static_file('index.html', root=file_path_root)


@route('/page/<item_id:path>')
def voice_page(item_id):
    print('open', item_id)
    html = """
    <html>
        <head>
            <meta charset="utf-8">
            <title>#{title}</title>
            <style type="text/css">
                #audio-player {
                    margin: 0 auto;
                    max-width: 600px;
                    width: 100%;
                    min-width: 300px;
                    min-height: 35px;
                }
                *, ::after, ::before {
                    box-sizing: inherit;
                }
                audio {
                    width: 300px;
                    height: 54px;
                }
                #player {
                    clear: both;
                    text-align: center;
                }
                body {
                    font-family: -apple-system,BlinkMacSystemFont,'Helvetica Neue','PingFang SC','Microsoft YaHei','Source Han Sans SC','Noto Sans CJK SC','WenQuanYi Micro Hei',sans-serif;
                    color: #333;
                    font-size: 14px;
                    word-wrap: break-word;
                    word-break: break-all;
                    background: #fafafa url(static/images/bg.jpg) center no-repeat;
                    background-attachment: fixed;
                    background-size: cover;
                    line-height: 1.6em;
                }
                html {
                    background-color: #fff;
                    font-size: 16px;
                    -moz-osx-font-smoothing: grayscale;
                    -webkit-font-smoothing: antialiased;
                    min-width: 300px;
                    overflow-x: hidden;
                    overflow-y: scroll;
                    text-rendering: optimizeLegibility;
                    -webkit-text-size-adjust: 100%;
                    -moz-text-size-adjust: 100%;
                    -ms-text-size-adjust: 100%;
                    text-size-adjust: 100%;
                }
                
                element.style {
                }
                #audio-player {
                    margin: 0 auto;
                    max-width: 600px;
                    width: 100%;
                    min-width: 300px;
                    min-height: 35px;
                }
                *, ::after, ::before {
                    box-sizing: inherit;
                }
                user agent stylesheet
                audio {
                    width: 300px;
                    height: 54px;
                }
                #player {
                    clear: both;
                    text-align: center;
                }
                body {
                    font-family: -apple-system,BlinkMacSystemFont,'Helvetica Neue','PingFang SC','Microsoft YaHei','Source Han Sans SC','Noto Sans CJK SC','WenQuanYi Micro Hei',sans-serif;
                    color: #333;
                    font-size: 14px;
                    word-wrap: break-word;
                    word-break: break-all;
                    background: #fafafa url(static/images/bg.jpg) center no-repeat;
                    background-attachment: fixed;
                    background-size: cover;
                    line-height: 1.6em;
                }
                body {
                    color: #4a4a4a;
                    font-size: 1em;
                    font-weight: 400;
                    line-height: 1.5;
                }
                body, button, input, select, textarea {
                    font-family: BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
                }
                html {
                    background-color: #fff;
                    font-size: 16px;
                    -moz-osx-font-smoothing: grayscale;
                    -webkit-font-smoothing: antialiased;
                    min-width: 300px;
                    overflow-x: hidden;
                    overflow-y: scroll;
                    text-rendering: optimizeLegibility;
                    -webkit-text-size-adjust: 100%;
                    -moz-text-size-adjust: 100%;
                    -ms-text-size-adjust: 100%;
                    text-size-adjust: 100%;
                }
                user agent stylesheet
                html {
                    color: -internal-root-color;
                }
                *, ::after, ::before {
                    box-sizing: inherit;
                }
                *, ::after, ::before {
                    box-sizing: inherit;
                }
            </style>
        </head>
        <body>
            <div id="player" class="radio-player">
                <div id="ajax-player" style="">
                    <audio id="audio-player" src="#{src}" autoplay="autoplay" controls="controls" preload="auto"></audio>
                </div>
            </div>
        </body>
    </html>
    """
    html = html.replace('#{title}', '播放' + item_id)
    html = html.replace('#{src}', '/listen/' + item_id)
    return html


@route("/listen/<item_id:path>")
def listen(item_id):
    print('listen:', item_id)
    response.content_type = 'audio/mpeg'
    response.set_header('access-control-allow-origin', '*')
    response.set_header('cache-control', 'no-cache')
    response.set_header('icy-metadata', '1')
    response.set_header('icy-pub', '0')
    response.set_header('pragma', 'no-cache')
    info = get_item_info(item_id)
    i = 0
    while True:
        if i >= len(info['contents']):
            if info['next_id'] is not None:
                info = get_item_info(str(info['next_id']))
                i = 0
            else:
                return
        text = info['contents'][i]
        i += 1
        b = synthesis(text, spd=SPD, per='0')
        yield b


# ######## WEBAPP ROUTERS ###############
@route('/')
def home():
    return template('<h1>Hello {{name}} !</h1><a href="/assets/code/pycrawlers/webapp_demo.py">View source</a><br />'
                    '<br /> <a href="javascript:milib.close()">>> Exit</a><br />'
                    '<a href="http://wiki.qpython.org/doc/program_guide/web_app/">>> About QPython Web App</a>',
                    name='QPython')


# ######## WEBAPP ROUTERS ###############
app = Bottle()
app.route('/', method='GET')(home)
app.route('/__exit', method=['GET', 'HEAD'])(__exit)
app.route('/__ping', method=['GET', 'HEAD'])(__ping)
app.route('/assets/<file_path:path>', method='GET')(server_static)
app.route('/np/', method='GET')(novel_index)
app.route('/np/<file_path:path>', method='GET')(novel)
app.route('/listen/<item_id:path>', method='GET')(listen)
app.route('/page/<item_id:path>', method='GET')(voice_page)

try:
    server = MyWSGIRefServer(host="127.0.0.1", port="8080")
    app.run(server=server,reloader=False, debug=True)
    # run(host='localhost', port=8080)
except (Exception) as ex:
    print("Exception: %s" % repr(ex))

# print(next(get_file_data('D:/video/肯普法/abc.txt')))


